/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.web.bean;

import com.jb.framework.PersistentContext;
import com.jb.framework.PersistentManager;
import com.jb.framework.Scope;
import com.jb.framework.pm.XMLPersistentManager;
import com.jb.web.application.configuration.Config;
import com.jb.web.bean.inspector.FilePropertyEditor;
import com.jb.web.bean.inspector.OptionPropertyEditor;
import com.jb.web.context.WebContext;
import com.jb.web.session.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Page component that contains the whole component tree displayed as a page
 *
 * @author Tim Wang
 * @version 1.0
 */
public class Page extends Region implements Scope{
    private transient Log log = LogFactory.getLog(getClass());
    static final long serialVersionUID = 123123L;
    public static String ROOT_NAME = "root";
    public static String PUBLIC = "public"; //can be view by any one
    public static String RESTRICT = "restrict"; //can be viewed only by authenticated user
    public static String PRIVATE = "private"; //can be viewed by only owner
    public static String GROUP = "group"; //can be viewed by friends

    static {
        publish(Page.class, GENERAL, "personalizable");
        publish(Page.class, GENERAL, "title");
        publish(Page.class, GENERAL, "icon", new FilePropertyEditor());
        publish(Page.class, GENERAL, "lang");
        publish(Page.class, GENERAL, "dir");
        publish(Page.class, STYLE, "overflow");
        publish(Page.class, GENERAL, "cached");
        publish(Page.class, EVENT_HANDLER, "onLoad");
        publish(Page.class, SECURITY, "access", 
                new OptionPropertyEditor("seeded:pageAccess"));
        publish(Page.class, SECURITY, "accessLevel");
        publish(Page.class, GENERAL, "errorPage");
        publish(Page.class, GENERAL, "designable");
        publish(Page.class, GENERAL, "keyWords");
        publish(Page.class, GENERAL, "align", new OptionPropertyEditor("seeded:cellAlign"));
    }

    private transient PersistentManager loader;
    private transient String ownerTop; 
    private String align;
    private boolean cached=true;
    //access access assigned to this page. See security for more details
    private String access = PRIVATE;

    //application name this page belongs
    private transient String application;

    //created by
    private String createdBy;

    //direction for language
    private String dir;

    //page name if error happens in this page
    private String errorPage;
    private String icon;

    //keywords can be associated with the page, mostly can be rendered for search engine
    private String keyWords;

    //language for this page
    private String lang;
    private String title;
    private UIComponentImpl contained;
    private boolean dialogMode;
    private boolean embedded;

    //indicate this page is opened as a dialog box
    private boolean personalized;

    //
    //if a page is private, the access level decides whether it can be displayed 
    // depends on user's access
    private int accessLevel;

    //created at
    private long createdAt;

    public void setAccess(String access) {
        this.access = access;
    }
    
    public String getAccess() {
        return access;
    }

    public void setAccessLevel(int accessLevel) {
        this.accessLevel = accessLevel;
    }

    public int getAccessLevel() {
        return accessLevel;
    }

    public void setApplication(String application) {
        this.application = application;
    }

    public String getApplication() {
        return application;
    }

    /**
     * Refuse to add another page to this page
     *
     * @param child object go tbe addded.
     *
     * @return false if the child is a page.
     */
    public boolean isChildAccepatble(BaseComponent child) {
        return !(child instanceof Page);
    }

    public void setContained(UIComponentImpl contained) {
        this.contained = contained;
    }

    public UIComponentImpl getContained() {
        return contained;
    }

    public void setCreatedAt(long createdAt) {
        this.createdAt = createdAt;
    }

    public long getCreatedAt() {
        return createdAt;
    }

    public void setCreatedBy(String pCreatedBy) {
        createdBy = pCreatedBy;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    /**
     * DOCUMENT ME!
     *
     * @param pContext DOCUMENT ME!
     */
    public void setDefault(PersistentContext pContext) {
        setHeight("400px");
        this.setDesignable(true);
    }

    /**
     * DOCUMENT ME!
     *
     * @param pDesignable DOCUMENT ME!
     */
    public void setDesignable(boolean pDesignable) {
        super.setDesignable(pDesignable);
    }

    public void setDialogMode(boolean dialogMode) {
        this.dialogMode = dialogMode;
    }

    public boolean isDialogMode() {
        return dialogMode;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public String getDir() {
        return dir;
    }

    public void setEmbedded(boolean embedded) {
        this.embedded = embedded;
    }

    public boolean isEmbedded() {
        return embedded;
    }

    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }

    public String getErrorPage() {
        return errorPage;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getIcon() {
        if(icon==null)
            return Config.getInstance().getIcon();
        return icon;
    }

    public void setKeyWords(String keyWords) {
        this.keyWords = keyWords;
    }

    public String getKeyWords() {
        return keyWords;
    }

    public void setLang(String lang) {
        this.lang = lang;
    }

    public String getLang() {
        return lang;
    }

    public void setLoader(PersistentManager loader) {
        this.loader = loader;
    }

    public PersistentManager getLoader() {
        return loader;
    }

    public boolean isReadOnly() {
        if ((loader == null) && this.isCloned()) {
            loader = ((Page)getClonedFrom()).getLoader();
        }

        return (loader != null) && loader.isReadOnly();
    }


    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
    /**
     * Tests whether a session has access to this page. Return false if
     * it does not
     * @param session current user session.
     * @return true if access is granted, else false.
     */
    public boolean isViewable(Session session) {
        if (isPublic() || session.getUser().getId().equals(ROOT_NAME)) {
            return true;
        }

        if (isPrivate() && session.isAuthenticated()) {
            if (getCreatedBy() == null) {
                setCreatedBy(ROOT_NAME);
            }

            return getCreatedBy().equals(session.getUser().getId());
        }

        if (isGroup() && session.isAuthenticated()) {
            return session.getUser().getAccess() <= this.getAccessLevel();
        }

        return session.isAuthenticated();
    }

    //
    //Called by view handler to load personalization inforation before
    //Renderring
    //

    private void loadPersonalization(WebContext ctx) {
        //
        //this is needed so that a page does need to load personalization every time.
        //however, there are times when personalization is needed.
        //
        if (!isPersonalizable() || !ctx.isRunTime()) {
            if (log.isDebugEnabled()) {
                log.debug("not personalizable!");
            }
            return;
        }

        if (!personalized) {
            if (log.isDebugEnabled()) {
                log.debug("Loading personalization");
            }
            PersistentManager pm = ctx.getPersonalPM();

            if (pm != null) {
                BaseComponent personalComponent = (BaseComponent) pm.findById(Page.class, this.getId());
                if (personalComponent != null) {
                    merge(ctx, personalComponent);
                }
            }

            personalized = true;

            return;
        }

        //bloggable page
        String ownerTop = ctx.getSession().getOwnerContentTop();

        if (!ownerTop.equals(this.ownerTop)) {
            //we need to restore the default first
            restoreDefault(ctx);
       //     super.loadPersonalization(ctx);
        }
    }
    
    protected void merge(WebContext ctx, BaseComponent component) {
        
        //no-op
    }
    
    public String toString() {
        return application + ":" + getId();
    }

    private boolean isGroup() {
        return GROUP.equalsIgnoreCase(getAccess());
    }

    private boolean isPrivate() {
        return PRIVATE.equalsIgnoreCase(getAccess());
    }

    private boolean isPublic() {
        return PUBLIC.equalsIgnoreCase(getAccess());
    }
    public void setAlign(String align) {
        this.align = align;
    }

    public String getAlign() {
        return align;
    }

    public void setCached(boolean param) {
        this.cached = param;
    }

    public boolean isCached() {
        return cached;
    }
}
